<template>
  <div class="c-form-detail">
    <div class="c-form-header" @click="handleFoldClick" v-if="!hiddenHeader">
      <div class="g-flex-start-center">
        <template v-if="!$slots.header">
          <CIcon v-if="getProps.icon" class="form-icon" :type="getProps.icon"></CIcon>
          <span class="form-title g-ml6">{{ getProps.title }}</span>
          <template v-if="getProps.desc">
            <div class="short-line"></div>
            <span class="form-desc">{{ getProps.desc }}</span>
          </template>
        </template>
        <slot name="header"></slot>
      </div>
      <CIcon
        v-if="!foldDisabled"
        class="fold__icon"
        :class="isFold ? 'fold__icon--up' : ''"
        type="c-xian-xia"
      ></CIcon>
    </div>
    <CCollapseTransition>
      <el-row v-show="!isFold" :gutter="gutter" class="g-mt32 g-pb20">
        <template v-for="schema in getSchema" :key="schema.field" :rules="schema?.rules">
          <el-col :span="schema.span || 24">
            <el-form-item
              class="c-form-item"
              :class="{ required: schema.required }"
              :label="schema.label"
              :prop="schema.field"
              :labelWidth="schema.labelWidth"
            >
              <template #label v-if="schema.tip || schema.tipSlot">
                <span>
                  {{ schema.label }}
                  <template v-if="schema.tipSlot">
                    <slot :name="schema.tipSlot"></slot>
                  </template>
                  <el-tooltip v-else :content="schema.tip" placement="top">
                    <CIcon type="c-xian-shuoming" class="tip-icon"></CIcon>
                  </el-tooltip>
                </span>
              </template>
              <template v-if="schema.slot">
                <slot :name="schema.slot"></slot>
              </template>
              <template v-else>
                <CFormItem
                  :searchBoxItem="schema"
                  :searchForm="modelVal"
                  @change="changeEvent"
                ></CFormItem>
              </template>
            </el-form-item>
          </el-col>
        </template>
      </el-row>
    </CCollapseTransition>
  </div>
</template>
<script lang="ts" setup>
import {
  inject,
  unref,
  ref,
  computed,
  withDefaults,
  defineProps,
  defineEmits,
  defineExpose,
  nextTick
} from 'vue';
import { IFormSchema, IFormGroupInstance } from './form-detail';
import CFormItem from './CFormItem.vue';
import CCollapseTransition from '@/components/CCollapseTransition/index.vue';
import { isArray, isObject, isString } from '@/utils/is';
import { deepMerge } from '@/utils/index';
import { cloneDeep, uniqBy } from 'lodash';

interface IFormDetailProps {
  modelValue?: any;
  schemas: IFormSchema[]; // 表单配置
  icon?: string;
  title: string; // 主标题
  desc?: string; // 描述
  disabled?: boolean;
  gutter?: number;
  hiddenHeader?: boolean; // 隐藏折叠头部
  foldDisabled?: boolean; // 是否禁用展开/收起
}
type Nullable<T> = T | null;
// all schema
const schemaRef = ref<Nullable<IFormSchema[]>>(null);
// all props
const innerPropsRef = ref<Partial<IFormDetailProps>>();
// all props
const getProps = computed(() => {
  return { ...props, ...unref(innerPropsRef) } as IFormDetailProps;
});
function setProps(props: Partial<IFormDetailProps>) {
  innerPropsRef.value = { ...unref(innerPropsRef), ...props };
  console.log('innerPropsRef', innerPropsRef.value);
}
async function updateSchema(data: Partial<IFormSchema> | Partial<IFormSchema>[]) {
  let updateData: Partial<IFormSchema>[] = [];
  if (isObject(data)) {
    updateData.push(data as IFormSchema);
  }
  if (isArray(data)) {
    updateData = [...data];
  }

  const hasField = updateData.every(item => Reflect.has(item, 'field') && item.field);

  if (!hasField) {
    new Error(
      'All children of the form Schema array that need to be updated must contain the `field` field'
    );
    return;
  }
  const schema: IFormSchema[] = [];
  updateData.forEach(item => {
    unref(getSchema.value).forEach(val => {
      if (val.field === item.field) {
        const newSchema = deepMerge(val, item);
        schema.push(newSchema as IFormSchema);
      } else {
        schema.push(val);
      }
    });
  });

  schemaRef.value = uniqBy(schema, 'field');
}

async function resetSchema(data: Partial<IFormSchema> | Partial<IFormSchema>[]) {
  let updateData: Partial<IFormSchema>[] = [];
  if (isObject(data)) {
    updateData.push(data as IFormSchema);
  }
  if (isArray(data)) {
    updateData = [...data];
  }

  const hasField = updateData.every(item => Reflect.has(item, 'field') && item.field);

  if (!hasField) {
    new Error(
      'All children of the form Schema array that need to be updated must contain the `field` field'
    );
    return;
  }
  schemaRef.value = updateData as IFormSchema[];
}
async function removeSchemaByFiled(fields: string | string[]): Promise<void> {
  const schemaList: IFormSchema[] = cloneDeep(unref(getSchema));
  if (!fields) {
    return;
  }

  let fieldList: string[] = isString(fields) ? [fields] : fields;
  if (isString(fields)) {
    fieldList = [fields];
  }
  for (const field of fieldList) {
    _removeSchemaByFiled(field, schemaList);
  }
  schemaRef.value = schemaList;
}
/**
 * @description: Delete based on field name
 */
function _removeSchemaByFiled(field: string, schemaList: IFormSchema[]): void {
  if (isString(field)) {
    const index = schemaList.findIndex(schema => schema.field === field);
    if (index !== -1) {
      delete modelVal.value[field];
      schemaList.splice(index, 1);
    }
  }
}
const hiddenHeader = computed(() => {
  return unref(getProps)?.hiddenHeader;
});
const getSchema = computed((): IFormSchema[] => {
  const schemas: IFormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
  return schemas as IFormSchema[];
});
const props = withDefaults(defineProps<IFormDetailProps>(), {
  modelValue: () => ({}),
  title: '标题',
  desc: '',
  gutter: 0,
  hiddenHeader: false
});

const formDetailGroup = inject<IFormGroupInstance>('CFormDetailGroup', {});
const isGroup = computed(() => formDetailGroup && formDetailGroup?.name === 'CFormDetailGroup');
const store = computed(() =>
  formDetailGroup ? formDetailGroup.modelVal?.value : props.modelValue
);
const modelVal = computed(() => {
  return isGroup.value ? store.value : props.modelValue;
});
const gutter = computed(() => {
  return isGroup.value ? formDetailGroup.formConfig?.gutter : props.gutter;
});
const emit = defineEmits(['update:modelValue', 'change']);
const isFold = ref(false);
const changeEvent = (value: any) => {
  if (isGroup.value) {
    formDetailGroup.changeEvent!(value);
  } else {
    emit('update:modelValue', value);
    nextTick(() => {
      emit('change', value);
    });
  }
};
const handleFoldClick = () => {
  if (!props.foldDisabled) {
    isFold.value = !isFold.value;
  }
};
defineExpose({
  setProps,
  updateSchema,
  removeSchemaByFiled,
  resetSchema
});
</script>
<style lang="scss" scoped>
@use '../styles/index.scss' as *;
</style>
